﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Drawing;
using System.Xml;
using HalconDotNet;
using Go;

namespace fhs
{
    class Vision_Line : VisionBase
    {
        public class Param : ParamBase
        {
            public class Line
            {
                public int exposureTime;
                public Roi.Rect hengLine;
                public Roi.Rect shuLine;
                public int threshold;
                public double sigma;
                public double deltaAngle;
                public bool hengTransition;
                public bool shuTransition;
                public bool metrologyLine;

                public void Load(string vision, string productName)
                {
                    try
                    {
                        string path = $"./VisionConfig/Product/{productName}/{vision}";
                        exposureTime = DefaultLoad.Load(0, $"{path}/ExposureTime.txt", 0);
                        hengLine.Row = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 0);
                        hengLine.Col = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 1);
                        hengLine.Len1 = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 2);
                        hengLine.Len2 = DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 3);
                        hengLine.Phi = FlatCalib.ToRad(DefaultLoad.Load(0.0, $"{path}/HengLine.txt", 4));
                        shuLine.Row = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 0);
                        shuLine.Col = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 1);
                        shuLine.Len1 = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 2);
                        shuLine.Len2 = DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 3);
                        shuLine.Phi = FlatCalib.ToRad(DefaultLoad.Load(0.0, $"{path}/ShuLine.txt", 4));
                        threshold = DefaultLoad.Load(60, $"{path}/Threshold.txt", 0);
                        sigma = DefaultLoad.Load(1.0, $"{path}/Sigma.txt", 0);
                        deltaAngle = FlatCalib.ToRad(DefaultLoad.Load(1.0, $"{path}/DeltaAngle.txt", 0));
                        hengTransition = DefaultLoad.Load(true, $"{path}/HengTransition.txt", 0);
                        shuTransition = DefaultLoad.Load(true, $"{path}/ShuTransition.txt", 0);
                        metrologyLine = DefaultLoad.Load(true, $"{path}/MetrologyLine.txt", 0);
                    }
                    catch (System.Exception ex)
                    {
                        LogMgr.Error($"{vision}参数读取异常{ex.Message}");
                    }
                }

                public void Save(string vision, string productName)
                {
                    try
                    {
                        string path = $"./VisionConfig/Product/{productName}/{vision}";
                        Directory.CreateDirectory(path);
                        File.WriteAllLines($"{path}/ExposureTime.txt", new string[] { exposureTime.ToString() });
                        File.WriteAllLines($"{path}/HengLine.txt", new string[] { hengLine.Row.ToString(), hengLine.Col.ToString(), hengLine.Len1.ToString(), hengLine.Len2.ToString(), FlatCalib.ToDeg(hengLine.Phi).ToString() });
                        File.WriteAllLines($"{path}/ShuLine.txt", new string[] { shuLine.Row.ToString(), shuLine.Col.ToString(), shuLine.Len1.ToString(), shuLine.Len2.ToString(), FlatCalib.ToDeg(shuLine.Phi).ToString() });
                        File.WriteAllLines($"{path}/Threshold.txt", new string[] { threshold.ToString() });
                        File.WriteAllLines($"{path}/Sigma.txt", new string[] { sigma.ToString() });
                        File.WriteAllLines($"{path}/DeltaAngle.txt", new string[] { FlatCalib.ToDeg(deltaAngle).ToString() });
                        File.WriteAllLines($"{path}/HengTransition.txt", new string[] { hengTransition.ToString() });
                        File.WriteAllLines($"{path}/ShuTransition.txt", new string[] { shuTransition.ToString() });
                        File.WriteAllLines($"{path}/MetrologyLine.txt", new string[] { metrologyLine.ToString() });
                    }
                    catch (System.Exception ex)
                    {
                        LogMgr.Error($"{vision}参数保存异常{ex.Message}");
                    }
                }
            }

            public Dictionary<VisionName.Camera, Line> VisionConfig = new Dictionary<VisionName.Camera, Line>();

            public override VisionName.Vision Name
            {
                get
                {
                    return VisionName.Vision.Line;
                }
            }

            public Param()
            {
                foreach (VisionName.Camera camera in Enum.GetValues(typeof(VisionName.Camera)))
                {
                    VisionConfig[camera] = new Line();
                }
            }

            public void Load(VisionName.Camera camera, string productName)
            {
                VisionConfig[camera].Load($"{Name}/{camera}", productName);
            }

            public override void Load(string productName)
            {
                try
                {
                    foreach (var item in VisionConfig)
                    {
                        item.Value.Load($"{Name}/{item.Key}", productName);
                    }
                }
                catch (System.Exception ex)
                {
                    LogMgr.Error($"{Name}参数读取异常{ex.Message}");
                }
            }

            public override void Save(string productName)
            {
                try
                {
                    foreach (var item in VisionConfig)
                    {
                        item.Value.Save($"{Name}/{item.Key}", productName);
                    }
                }
                catch (System.Exception ex)
                {
                    LogMgr.Error($"{Name}参数保存异常{ex.Message}");
                }
            }
        }

        public override VisionName.Vision Name
        {
            get
            {
                return VisionName.Vision.Line;
            }
        }

        public class Result : ResultBase
        {
            public bool error;
            public MyPoint mark;
            public MyPoint shuMark;
            public MyPoint hengMark;
            public HObject image;
        }

        public class Option : OptionBase
        {
            public VisionName.Camera camera;
            public HObject image;
            public int delay;
        }

        protected override async Task<ResultBase> RefPriview(CameraControl mainView, HObject[] images, ParamBase param)
        {
            mainView.Clear();
            viewObjects.value.drawObjects.Clear();
            viewObjects.value.drawTexts.Clear();
            mainView.ShowImage(images[0]);
            return null;
        }

        public VisionName.Camera PreviewCamera = (VisionName.Camera)(-1);

        protected override async Task<ResultBase> Translate(bool isPreview, CameraControl mainView, ParamBase paramBase, OptionBase optionBase)
        {
            ViewObjects viewObjects = this.viewObjects.value;
            Option option = (Option)optionBase;
            Param param = (Param)paramBase;
            if (null == param)
            {
                param = new Param();
                await generator.send_task(() => param.Load(ProductMgr.currProduct));
            }
            VisionName.Camera cameraName = null != option ? option.camera : PreviewCamera;
            Param.Line lineParam = param.VisionConfig[cameraName];
            viewObjects.drawObjects.Clear();
            viewObjects.drawTexts.Clear();
            long beginTick = system_tick.ms;
            if (isPreview)
            {
                try
                {
                    CameraBase camera = CameraMgr.cameras[cameraName];
                    await generator.send_service(camera.optionWork.service, () => camera.SetExposureTime(lineParam.exposureTime));
                    if (null != option && option.delay > 0)
                    {
                        await generator.sleep(option.delay);
                    }
                    HObject snapImage = await generator.send_service(camera.optionWork.service, () => camera.Snap());
                    LogMgr.Info($"{cameraName} 取图用时 {system_tick.ms - beginTick}ms");
                    if (null != snapImage)
                    {
                        mainView.ShowImage(snapImage);
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "Main", obj = snapImage });
                    }
                    else
                    {
                        mainView.Clear();
                        LogMgr.Error($"{cameraName}取图失败");
                    }
                }
                catch (generator.stop_exception) { throw; }
                catch (Exception)
                {
                    mainView.Clear();
                    LogMgr.Error($"{cameraName}取图失败");
                }
                return null;
            }
            HObject Image = option.image;
            if (null == option.image)
            {
                try
                {
                    CameraBase camera = CameraMgr.cameras[cameraName];
                    await generator.send_service(camera.optionWork.service, () => camera.SetExposureTime(lineParam.exposureTime));
                    if (option.delay > 0)
                    {
                        await generator.sleep(option.delay);
                    }
                    Image = await generator.send_service(camera.optionWork.service, () => camera.Snap());
                    LogMgr.Info($"{cameraName} 取图用时 {system_tick.ms - beginTick}ms");
                }
                catch (generator.stop_exception) { throw; }
                catch (Exception) { }
                if (null == Image)
                {
                    mainView.Clear();
                    LogMgr.Error($"{cameraName}取图失败");
                    return null;
                }
            }
            mainView.Clear();
            mainView.ShowImage(Image);
            viewObjects.drawObjects.AddLast(new ObjectShow { text = "Main", obj = Image });
            Result result = new Result { image = Image };
            generator.lock_shield();
            bool lineOk = await generator.send_service(genWork.service, delegate ()
            {
                try
                {
                    AOI.FindLineResult hengRes, shuRes;
                    if (lineParam.metrologyLine)
                    {
                        hengRes = AOI.FindLine2(Image, lineParam.hengLine, 30, lineParam.sigma, lineParam.threshold, lineParam.hengTransition);
                        shuRes = AOI.FindLine2(Image, lineParam.shuLine, 30, lineParam.sigma, lineParam.threshold, lineParam.shuTransition);
                    }
                    else
                    {
                        hengRes = AOI.FindLine(Image, lineParam.hengLine, 30, lineParam.sigma, lineParam.threshold, lineParam.hengTransition);
                        shuRes = AOI.FindLine(Image, lineParam.shuLine, 30, lineParam.sigma, lineParam.threshold, lineParam.shuTransition);
                    }
                    if (null != hengRes.lineContour && null != shuRes.lineContour)
                    {
                        HTuple angle, row, col, isP;
                        HObject startCross1, startCross2, interCross;
                        AOI.FindLineResult markRes = new AOI.FindLineResult();
                        HOperatorSet.GenCrossContourXld(out startCross1, hengRes.line.value1.Row, hengRes.line.value1.Col, 60, FlatCalib.ToRad(45));
                        HOperatorSet.GenCrossContourXld(out startCross2, shuRes.line.value1.Row, shuRes.line.value1.Col, 60, FlatCalib.ToRad(45));
                        HOperatorSet.ConcatObj(hengRes.roi, shuRes.roi, out markRes.roi);
                        HOperatorSet.ConcatObj(hengRes.lineContour, shuRes.lineContour, out markRes.lineContour);
                        HOperatorSet.ConcatObj(hengRes.measCross, shuRes.measCross, out markRes.measCross);
                        HOperatorSet.ConcatObj(markRes.lineContour, startCross1, out markRes.lineContour);
                        HOperatorSet.ConcatObj(markRes.lineContour, startCross2, out markRes.lineContour);
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "MeasROIs", obj = markRes.roi, color = Color.Lime, margin = true });
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "LineContour", obj = markRes.lineContour, color = Color.Red, margin = true });
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "MeasCross", obj = markRes.measCross, color = Color.Blue, margin = true });
                        HOperatorSet.AngleLl(
                            hengRes.line.value1.Row, hengRes.line.value1.Col, hengRes.line.value2.Row, hengRes.line.value2.Col,
                            shuRes.line.value1.Row, shuRes.line.value1.Col, shuRes.line.value2.Row, shuRes.line.value2.Col, out angle);
                        HOperatorSet.IntersectionLl(
                            hengRes.line.value1.Row, hengRes.line.value1.Col, hengRes.line.value2.Row, hengRes.line.value2.Col,
                            shuRes.line.value1.Row, shuRes.line.value1.Col, shuRes.line.value2.Row, shuRes.line.value2.Col, out row, out col, out isP);
                        result.mark = new MyPoint { Row = row, Col = col };
                        result.hengMark = 0.5 * (hengRes.line.value1 + hengRes.line.value2);
                        result.shuMark = 0.5 * (shuRes.line.value1 + shuRes.line.value2);
                        HOperatorSet.GenCrossContourXld(out interCross, row, col, 60, FlatCalib.ToRad(45));
                        viewObjects.drawObjects.AddLast(new ObjectShow { text = "InterCross", obj = interCross, color = Color.Blue, margin = true });
                        double deltaAngle = Math.Abs(90.0 - Math.Abs(FlatCalib.NorAngle(FlatCalib.ToDeg(angle))));
                        bool deltaOk = deltaAngle <= FlatCalib.ToDeg(lineParam.deltaAngle);
                        if (!deltaOk)
                        {
                            LogMgr.Error($"{cameraName}角差异常，定位失败");
                        }
                        viewObjects.drawTexts.AddLast(WindowTexts.Make($"{deltaAngle.ToString("0.0")},({(int)row.D},{(int)col.D})", true, (int)row.D, (int)col.D, deltaOk ? Color.Lime : Color.Red, false, Color.White));
                        return deltaOk;
                    }
                    LogMgr.Error($"{cameraName}定位失败");
                    if (null != hengRes.roi) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengROIs", obj = hengRes.roi, color = Color.Lime, margin = true });
                    if (null != shuRes.roi) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuROIs", obj = shuRes.roi, color = Color.Lime, margin = true });
                    if (null != hengRes.lineContour) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengLineContour", obj = hengRes.lineContour, color = Color.Red, margin = true });
                    if (null != shuRes.lineContour) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuLineContour", obj = shuRes.lineContour, color = Color.Red, margin = true });
                    if (null != hengRes.measCross) viewObjects.drawObjects.AddLast(new ObjectShow { text = "HengMeasCross", obj = hengRes.measCross, color = Color.Blue, margin = true });
                    if (null != shuRes.measCross) viewObjects.drawObjects.AddLast(new ObjectShow { text = "ShuMeasCross", obj = shuRes.measCross, color = Color.Blue, margin = true });
                    return false;
                }
                catch (Exception ec)
                {
                    LogMgr.File($"{cameraName}定位失败{ec.Message}\r\n{ec.StackTrace}");
                    return false;
                }
            });
            await generator.unlock_shield();
            LogMgr.Info($"{cameraName} 共计用时 {system_tick.ms - beginTick}ms");
            result.error = !lineOk;
            return result;
        }

        public override ParamBase LoadParam(string productName)
        {
            Param result = new Param();
            result.Load(productName);
            return result;
        }
    }
}
